package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import com.cloudfun.campusshare.common.constant.*;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.dto.*;
import com.cloudfun.campusshare.common.model.vo.*;
import com.cloudfun.campusshare.common.response.BaseResult;
import com.cloudfun.campusshare.common.response.BaseResultFactory;
import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.common.security.AuthenticationHolder;
import com.cloudfun.campusshare.entity.*;
import com.cloudfun.campusshare.repo.AuthOrgEntityRepo;
import com.cloudfun.campusshare.repo.DeviceEntityRepo;
import com.cloudfun.campusshare.repo.UserEntityRepo;
import com.cloudfun.campusshare.repo.UserRoleEntityRepo;
import com.cloudfun.campusshare.service.*;
import com.cloudfun.campusshare.util.*;
import com.cloudfun.campusshare.util.hsq.StringUtil;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author linyangping
 * @date 2019/10/21
 */
@Slf4j
@Service
public class UserServiceImpl extends CreatorPersistenceService implements UserService, ApplicationContextAware {

    @Autowired
    private UserEntityRepo userEntityRepo;

    @Autowired
    private VerifyCodeService verifyCodeService;

    @Autowired
    private RoleService roleService;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private AuthOrgEntityRepo authOrgEntityRepo;

    @Autowired
    private AttachmentService attachmentService;

    @Autowired
    private DeviceOrderService deviceOrderService;

    @Autowired
    private UserAuthService userAuthService;

    @Autowired
    private UserRelService userRelService;

    @Autowired
    private AuthorityService authorityService;

    @Autowired
    private AuthService authService;

    private ApplicationContext applicationContext;
    @Resource
    private UserRoleEntityRepo userRoleEntityRepo;

    @Resource
    private DeviceEntityRepo deviceEntityRepo;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    protected Class<?> getEntityClass() {
        return UserEntity.class;
    }

    @Override
    @SneakyThrows
    @Transactional
    public BaseResult<?> register(RegisterVO registerVO) {
        // 判断是否勾选了协议
        if (!registerVO.isAgree()) {
            return BaseResultFactory.produceEmptyResult(Code.AGREEMENT);
        }
        // 判断手机验证码
        BaseResult<?> result = verifyCodeService.verifyVerifyCode(VerifyType.REGISTER, registerVO.getPhone(), registerVO.getInputCode());
        if (result.getCode() != Code.SUCCESS.getCode()) {
            return result;
        }
        // 判断两次密码是否一致
        if (!registerVO.getPassword().equals(registerVO.getPasswordConfirm())) {
            return BaseResultFactory.produceEmptyResult(Code.PASSWORD_DIFF);
        }
        // 判断用户是否已经存在
        UserEntity userEntity = userEntityRepo.findbyPhone(registerVO.getPhone());
        if (!ObjectUtils.isEmpty(userEntity)) {
            return BaseResultFactory.produceEmptyResult(Code.PHONE_EXIST);
        }
        // 判断企业名是否已存在
        if (StringUtils.isNotBlank(registerVO.getEnterpriseName())) {
            userEntity = userEntityRepo.findByEnterpriseName(registerVO.getEnterpriseName());
            if (!ObjectUtils.isEmpty(userEntity)) {
                return BaseResultFactory.produceEmptyResult(Code.ENTERPRISE_NAME_EXISTS);
            }
        }
        // 存储用户信息
        userEntity = new UserEntity();
        userEntity.setPhone(registerVO.getPhone());
        userEntity.setStatus(AccountStatus.VALID.getCode());
        userEntity.setActivateStatus(AccountActiveStatus.ACTIVATED);
        long count = userEntityRepo.count();
        if (StringUtils.isBlank(registerVO.getUserName())) {
            userEntity.setUsername("商户" + StringUtils.leftPad(String.valueOf(count), 5, "0"));
        } else {
            userEntity.setUsername(registerVO.getUserName());
        }
        userEntity.setEnterpriseName(registerVO.getEnterpriseName());
        userEntity.setEmail(registerVO.getEmail());
        userEntity.setAuthStatus(AuthStatus.UNAUTH.getCode());
        userEntity.setEmailStatus(AccountActiveStatus.WAIT_ACTIVATED.getCode());
        userEntity.setErrorCount(0);
        userEntityRepo.save(userEntity);
        // 存储认证信息
        UserAuthDTO userAuthDTO = new UserAuthDTO();
        userAuthDTO.setUserId(userEntity.getId());
        userAuthDTO.setIdentityType(IdentityType.PHONE);
        userAuthDTO.setIdentifier(userEntity.getPhone());
        userAuthDTO.setCredential(EncryptUtil.encodePassword(registerVO.getPassword()));
        userAuthService.create(userAuthDTO);
        // 绑定角色
        RoleType roleType = registerVO.getRoleType();
        roleService.bindRoleTypes(userEntity.getId(), Arrays.asList(roleType));

        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    @Transactional
    public String register(RegisterVO registerVO, UserAuthDTO userAuthDTO) {
        String phone = registerVO.getPhone();
        // 判断用户是否已经存在
        UserEntity userEntity = userEntityRepo.findbyPhone(registerVO.getPhone());
        if (ObjectUtils.isEmpty(userEntity)) {
            // 存储用户信息
            userEntity = new UserEntity();
            userEntity.setPhone(phone);
            userEntity.setUsername(registerVO.getUserName());
            userEntity.setAuthStatus(AuthStatus.UNAUTH.getCode());
            userEntity.setErrorCount(0);
            userEntity.setStatus(AccountStatus.VALID.getCode());
            userEntity.setActivateStatus(AccountActiveStatus.ACTIVATED);
            userEntityRepo.save(userEntity);
        }
        this.afterUserCreate(userEntity.getId(), registerVO.getRoleType(), userAuthDTO);
        return userEntity.getId();
    }

    private void afterUserCreate(String userId, RoleType roleType, UserAuthDTO userAuthDTO) {
        // 存储认证信息
        userAuthDTO.setUserId(userId);
        userAuthService.create(userAuthDTO);
        // 绑定角色
        roleService.bindRoleTypes(userId, Arrays.asList(roleType));
    }

    @Override
    public BaseResult<?> getUser(String id) {
        // 获取用户基本信息
        Optional<UserEntity> userEntityOptional = userEntityRepo.findById(id);
        if (ObjectUtils.isEmpty(userEntityOptional)) {
            return BaseResultFactory.produceEmptyResult(Code.INVALID_USER);
        }
        UserEntity userEntity = userEntityOptional.get();
        UserVO userVO = new UserVO();
        userVO.setEmail(userEntity.getEmail());
        userVO.setAuthType(userEntity.getAuthType());
        userVO.setAuthStatus(userEntity.getAuthStatus());
        // 如果当前用户未认证 则寻找其所属运营商是否有认证
        if (AuthStatus.UNAUTH.getCode() == userEntity.getAuthStatus()) {
            UserEntity merchant = userRelService.getUserMerchant(userEntity.getId());
            if (null != merchant && merchant.getAuthStatus() == AuthStatus.AUTH_SUCCESS.getCode()) {
                userVO.setAuthStatus(AuthStatus.AUTH_SUCCESS.getCode());
            }
        }
        userVO.setPhone(userEntity.getPhone());
        userVO.setId(userEntity.getId());
        userVO.setPhone(userEntity.getPhone());
        userVO.setCreateTime(userEntity.getCreateTime());
        userVO.setUsername(userEntity.getUsername());
        userVO.setEnterpriseName(userEntity.getEnterpriseName());
        userVO.setStatus(userEntity.getStatus());
        userVO.setActivateStatus(userEntity.getActivateStatus());
        // 角色
        Set<String> roleTypes = roleService.getRoleTypes(AuthenticationHolder.getUserId());
        userVO.setRole(roleTypes);
        // 权限
        List<AuthorityEntity> authorityEntities = authorityService.getUserAuthorities(AuthenticationHolder.getUserId());
        List<AuthorityEntity> menus = authorityEntities.stream().filter(a -> "-1".equals(a.getParentId())).collect(Collectors.toList());
        userVO.setHeadPageName(menus.get(0).getName());
        Set<String> authorities = authorityEntities.stream().map(AuthorityEntity::getName).collect(Collectors.toSet());
        userVO.setAuthorities(authorities);

        return BaseResultFactory.produceResult(Code.SUCCESS, userVO);

    }

    @Override
    @Transactional
    public BaseResult<?> modify(UserInfoModifyVO userInfoModifyVO) {
        String userId = AuthenticationHolder.getUserId();
        UserEntity userEntity = userEntityRepo.findById(userId).orElseThrow(() -> new BusinessException(Code.NODATA));
        if (StringUtils.isNotBlank(userInfoModifyVO.getEnterpriseName()) && !userEntity.getEnterpriseName().equals(userInfoModifyVO.getEnterpriseName())) {
            UserEntity userByEnterpriseName = userEntityRepo.findByEnterpriseName(userInfoModifyVO.getEnterpriseName());
            if (!ObjectUtils.isEmpty(userByEnterpriseName)) {
                return BaseResultFactory.produceEmptyResult(Code.ENTERPRISE_NAME_EXISTS);
            }
            this.afterEnterpriseNameModified(userEntity.getId(), userInfoModifyVO.getEnterpriseName());
        }
        if (StringUtils.isNotBlank(userInfoModifyVO.getPhone()) && !userEntity.getPhone().equals(userInfoModifyVO.getPhone())) {
            UserEntity userByPhone = userEntityRepo.findbyPhone(userInfoModifyVO.getPhone());
            if (!ObjectUtils.isEmpty(userByPhone)) {
                return BaseResultFactory.produceEmptyResult(Code.PHONE_EXIST);
            }
            this.afterPhoneModified(userEntity.getId(), userInfoModifyVO.getPhone());
        }
        if (StringUtils.isNotBlank(userInfoModifyVO.getUsername()) && !userEntity.getUsername().equals(userInfoModifyVO.getUsername())) {
            this.afterUsernameModified(userEntity.getId(), userInfoModifyVO.getUsername());
        }
        BeanUtil.copyProperties(userInfoModifyVO, userEntity, true, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
        userEntityRepo.save(userEntity);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    @Transactional
    public BaseResult<?> additionInfo(UserAdditionInfoVO additionInfoVO) {
        String userId = AuthenticationHolder.getUserId();
        UserEntity userEntity = userEntityRepo.findById(userId).orElseThrow(() -> new BusinessException(Code.NODATA));
        if (StringUtils.isNotBlank(additionInfoVO.getPhone())) {
            UserEntity userByPhone = userEntityRepo.findbyPhone(additionInfoVO.getPhone());
            if (!ObjectUtils.isEmpty(userByPhone) && !userByPhone.getId().equals(userId)) {
                return BaseResultFactory.produceEmptyResult(Code.PHONE_EXIST);
            }
            this.afterPhoneModified(userEntity.getId(), additionInfoVO.getPhone());
        }
        if (StringUtils.isNotBlank(additionInfoVO.getEnterpriseName())) {
            UserEntity userByEnterpriseName = userEntityRepo.findByEnterpriseName(additionInfoVO.getEnterpriseName());
            if (!ObjectUtils.isEmpty(userByEnterpriseName) && !userByEnterpriseName.getId().equals(userId)) {
                return BaseResultFactory.produceEmptyResult(Code.ENTERPRISE_NAME_EXISTS);
            }
            this.afterEnterpriseNameModified(userEntity.getId(), additionInfoVO.getEnterpriseName());
        }
        BeanUtil.copyProperties(additionInfoVO, userEntity);
        userEntityRepo.save(userEntity);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    public BaseResult<Long> countAll() {
        return BaseResultFactory.produceResult(Code.SUCCESS, userEntityRepo.count());
    }

    @Override
    public List<UserSimpleVO> getAuthenticatedSimpleUserByRoleType(RoleType roleType) {
        if (roleType.equals(RoleType.ADMIN) && !AuthenticationHolder.isAdmin()) {
            return new ArrayList<>();
        }
        StringBuilder sqlBuilder = new StringBuilder("select u.id,u.enterprise_name from user u where u.auth_status=2 and exists(" +
                "select ur.id from user_role ur where ur.role_id=(select r.id from role r where r.TYPE=:roleType) and ur.user_id=u.id)");
        Query nativeQuery = entityManager.createNativeQuery(sqlBuilder.toString());
        nativeQuery.setParameter("roleType", roleType.name());
        List<Object[]> resultList = nativeQuery.getResultList();
        return resultList.stream().map(objs -> {
            String userId = (String) objs[0];
            String enterpriseName = (String) objs[1];
            return new UserSimpleVO(userId, enterpriseName, null);
        }).collect(Collectors.toList());
    }

    @Override
    public Long countAuthenticatedUserByRoleType(RoleType roleType) {
        if (roleType.equals(RoleType.ADMIN) && !AuthenticationHolder.isAdmin()) {
            return NumberUtils.LONG_ZERO;
        }
        StringBuilder sqlBuilder = new StringBuilder("select count(*) from user u where u.auth_status=2 and exists(" +
                "select ur.id from user_role ur where ur.role_id=(select r.id from role r where r.TYPE=:roleType) and ur.user_id=u.id)");
        Query nativeQuery = entityManager.createNativeQuery(sqlBuilder.toString());
        nativeQuery.setParameter("roleType", roleType.name());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(nativeQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        return count.longValue();
    }

    @Override
    @Transactional
    public BaseResult<?> resetPasswordByOldPassword(ResetPasswordByPassVO resetPasswordByPassVO) {
        UserAuthEntity userAuthEntity = userAuthService.getByUserIdAndType(AuthenticationHolder.getUserId(), IdentityType.PHONE);
        if (null == userAuthEntity) {
            return BaseResultFactory.produceEmptyResult(Code.INVALID_USER);
        }
        // 判断旧密码是否一致
        if (!StringUtils.equals(userAuthEntity.getCredential(), EncryptUtil.encodePassword(resetPasswordByPassVO.getOldPassword()))) {
            return BaseResultFactory.produceEmptyResult(Code.ERROR_PASSWORD_OLD);
        }
        return this.resetPassword(resetPasswordByPassVO.getPassword(), resetPasswordByPassVO.getPasswordConfirm(), userAuthEntity);
    }

    @Override
    @Transactional
    public BaseResult<?> resetPasswordByVerifyCode(ResetPasswordByCodeVO resetPasswordByCodeVO) {
        UserAuthEntity userAuthEntity = userAuthService.getByIdentityTypeAndIdentifier(IdentityType.PHONE, resetPasswordByCodeVO.getPhone());
        if (null == userAuthEntity) {
            return BaseResultFactory.produceEmptyResult(Code.INVALID_USER);
        }
        // 判断手机验证码
        BaseResult<?> result = verifyCodeService.verifyVerifyCode(VerifyType.RESET, resetPasswordByCodeVO.getPhone(), resetPasswordByCodeVO.getVerifyCode());
        if (result.getCode() != Code.SUCCESS.getCode()) {
            return result;
        }
        return this.resetPassword(resetPasswordByCodeVO.getPassword(), resetPasswordByCodeVO.getPasswordConfirm(), userAuthEntity);
    }

    @Override
    public BorrowerDetailVO borrowerDetail(String borrowerId) {
        UserEntity userEntity = userEntityRepo.findById(borrowerId).orElse(null);
        if (null == userEntity) {
            return null;
        }
        BorrowerDetailVO borrowerDetailVO = new BorrowerDetailVO();
        borrowerDetailVO.setId(userEntity.getId());
        borrowerDetailVO.setEnterpriseName(userEntity.getEnterpriseName());
        borrowerDetailVO.setEnterpriseType(RoleType.OPERATIONAL_MERCHANT.name());
        AuthOrgEntity authOrgEntity = authOrgEntityRepo.findTopByUserIdOrderByCreateTimeDesc(userEntity.getId());
        if (null != authOrgEntity) {
            borrowerDetailVO.setDetailAddr(authOrgEntity.getDetailAddr());
            List<AttachVO> attachVOList = attachmentService.getAttachment(authOrgEntity.getId(), Arrays.asList(AttachType.LICENSE));
            if (CollectionUtil.isNotEmpty(attachVOList)) {
                borrowerDetailVO.setLicenseUrl(attachVOList.get(0).getUrl());
            }
        }
        return borrowerDetailVO;
    }

    @Override
    public MerchantFIDetailVO omDetail(String borrowerId) {
        return getEnterpriseDetail(borrowerId, RoleType.OPERATIONAL_MERCHANT);
    }

    @Override
    public PageResult<BorrowerListVO> searchBorrower(String value, Pageable pageable) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT l.borrower_id,u.enterprise_name,count(DISTINCT d.id)," +
                "count(DISTINCT p.id) FROM loan l JOIN user u ON l.borrower_id=u.id JOIN project p on l.id=p.loan_id" +
                " LEFT JOIN device d ON p.id=d.project_id  where l.lender_id=:lenderId  and l.status!='CREATING' and p.status='BUSINESS'");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and u.enterprise_name like :value");
        }
        pageSqlBuilder.append(" GROUP BY l.borrower_id,u.enterprise_name limit :start,:end");
        StringBuilder totalNumSqlBuilder = new StringBuilder("select count(*) from (select l.borrower_id from loan l " +
                "JOIN user u ON l.borrower_id=u.id JOIN  project p on l.id=p.loan_id where l.lender_id=:lenderId and l.status!='CREATING' and p.status='BUSINESS'");
        if (StringUtils.isNotBlank(value)) {
            totalNumSqlBuilder.append(" and u.enterprise_name like :value");
        }
        totalNumSqlBuilder.append(" GROUP BY l.borrower_id,u.enterprise_name) as a");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("lenderId", AuthenticationHolder.getUserId());
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();
        List<BorrowerListVO> borrowerListVOList = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Long deviceCount = ((BigInteger) tuple[2]).longValue();
            Long projectCount = ((BigInteger) tuple[3]).longValue();
            BorrowerListVO borrowerListVO = new BorrowerListVO();
            borrowerListVO.setBorrowerId(userId);
            borrowerListVO.setEnterpriseName(enterpriseName);
            borrowerListVO.setDeviceCount(deviceCount);
            borrowerListVO.setProjectCount(projectCount);
            return borrowerListVO;
        }).collect(Collectors.toList());
        Set<String> projectId = this.searchBorrowerProjectIds(value);
        Map<String, String> userIdOrderTotalMap = deviceOrderService.getUserIdOrderTotalMap(projectId);
        borrowerListVOList.forEach(borrowerListVO -> {
            borrowerListVO.setOperationAmount(userIdOrderTotalMap.get(borrowerListVO.getBorrowerId()));
        });

        PageResult<BorrowerListVO> pageResult = new PageResult<>();
        pageResult.setElements(borrowerListVOList);

        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            totalNumQuery.setParameter("value", "%" + value + "%");
        }
        totalNumQuery.setParameter("lenderId", AuthenticationHolder.getUserId());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(totalNumQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        pageResult.setTotalSize(count.longValue());
        return pageResult;
    }

    @Override
    public PageResult<BorrowerHistoryListVO> getHistoryBorrowerList(Pageable pageable, String keyword) {
        StringBuilder sb = new StringBuilder("select l.borrower_id,u.enterprise_name,count(distinct p.id),sum(l.issue_amount) from loan l" +
                " JOIN user u on l.borrower_id=u.id join project p on p.loan_id=l.id WHERE l.lender_id=:lenderId and not exists(" +
                "select p.id from project p where p.id=l.id and p.status='BUSINESS' and p.status='PREPARE')");
        if (!StringUtils.isBlank(keyword)) {
            sb.append(" and u.enterprise_name like :keyword ");
        }
        sb.append(" GROUP BY l.borrower_id,u.enterprise_name limit :start,:end");
        Query query = entityManager.createNativeQuery(sb.toString());
        query.setParameter("lenderId", AuthenticationHolder.getUserId());
        if (!StringUtils.isBlank(keyword)) {
            query.setParameter("keyword", "%" + keyword + "%");
        }
        query.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        query.setParameter("end", pageable.getPageSize());
        List<Object[]> resultList = query.getResultList();
        List<BorrowerHistoryListVO> result = resultList.stream()
                .map(x -> {
                    String borrowerId = (String) x[0];
                    String enterpriseName = (String) x[1];
                    BigInteger projectCount = (BigInteger) x[2];
                    if (null == projectCount) {
                        projectCount = BigInteger.ZERO;
                    }
                    BigDecimal actualProvideAmount = (BigDecimal) x[3];
                    if (actualProvideAmount == null) {
                        actualProvideAmount = BigDecimal.ZERO;
                    }
                    return BorrowerHistoryListVO.builder().borrowerId(borrowerId).enterpriseName(enterpriseName)
                            .projectCount(projectCount.longValue()).actualProvideAmount(actualProvideAmount.doubleValue()).build();
                }).collect(Collectors.toList());

        StringBuilder countSql = new StringBuilder("select count(*) from loan l JOIN user u on l.borrower_id=u.id" +
                " WHERE l.lender_id=:lenderId and not exists(select p.id from project p where p.id=l.id and p.status='BUSINESS' and p.status='PREPARE')");
        if (!StringUtils.isBlank(keyword)) {
            countSql.append(" and u.enterprise_name like :keyword ");
        }
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        countQuery.setParameter("lenderId", AuthenticationHolder.getUserId());
        if (!StringUtils.isBlank(keyword)) {
            countQuery.setParameter("keyword", "%" + keyword + "%");
        }
        BigInteger res = (BigInteger) QueryUtil.getSingleResult(countQuery);
        if (null == res) {
            res = BigInteger.ZERO;
        }
        return new PageResult<>(res.longValue(), result);
    }

    private Set<String> searchBorrowerProjectIds(String value) {
        StringBuilder sqlBuilder = new StringBuilder("select p.id from project p JOIN user u ON p.user_id=u.id" +
                " join loan l on l.id=p.loan_id where l.lender_id=:lenderId and l.status!='CREATING'");
        if (StringUtils.isNotBlank(value)) {
            sqlBuilder.append(" and u.enterprise_name like :value");
        }
        Query nativeQuery = entityManager.createNativeQuery(sqlBuilder.toString());
        if (StringUtils.isNotBlank(value)) {
            nativeQuery.setParameter("value", "%" + value + "%");
        }
        nativeQuery.setParameter("lenderId", AuthenticationHolder.getUserId());
        List<String> resultList = nativeQuery.getResultList();
        return new HashSet<>(resultList);
    }

    @Override
    public PageResult<OMListVO> searchOMs(String value, Pageable pageable) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.enterprise_name,u.auth_status,count(DISTINCT d.id)," +
                "count(DISTINCT p.id) FROM user u LEFT JOIN device d ON u.id=d.user_id LEFT JOIN project p ON u.id=p.user_id where EXISTS (" +
                "SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and u.enterprise_name like :value");
        }
        pageSqlBuilder.append(" GROUP BY u.id,u.enterprise_name,u.auth_status limit :start,:end");
        StringBuilder totalNumSqlBuilder = new StringBuilder("select count(u.id) from user u where EXISTS " +
                "(SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            totalNumSqlBuilder.append(" and u.enterprise_name like :value");
        }
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();
        List<OMListVO> borrowerListVOList = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Integer authStatus = (Integer) tuple[2];
            Long deviceCount = ((BigInteger) tuple[3]).longValue();
            Long projectCount = ((BigInteger) tuple[4]).longValue();
            OMListVO omListVO = new OMListVO();
            omListVO.setBorrowerId(userId);
            omListVO.setEnterpriseName(enterpriseName);
            omListVO.setAuthStatus(authStatus);
            omListVO.setDeviceCount(deviceCount);
            omListVO.setProjectCount(projectCount);
            return omListVO;
        }).collect(Collectors.toList());
        Set<String> projectIds = this.searchOMProjectIds(value);
        Map<String, String> userIdOrderTotalMap = deviceOrderService.getUserIdOrderTotalMap(projectIds);
        borrowerListVOList.forEach(omListVO -> {
            omListVO.setOperationAmount(userIdOrderTotalMap.get(omListVO.getBorrowerId()));
        });

        PageResult<OMListVO> pageResult = new PageResult<>();
        pageResult.setElements(borrowerListVOList);

        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            totalNumQuery.setParameter("value", "%" + value + "%");
        }
        totalNumQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(totalNumQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        pageResult.setTotalSize(count.longValue());
        return pageResult;
    }

    private Set<String> searchOMProjectIds(String value) {
        StringBuilder sqlBuilder = new StringBuilder("SELECT DISTINCT p.id FROM user u LEFT JOIN project p ON u.id=p.user_id" +
                " where EXISTS (SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            sqlBuilder.append(" and u.enterprise_name like :value");
        }
        Query nativeQuery = entityManager.createNativeQuery(sqlBuilder.toString());
        if (StringUtils.isNotBlank(value)) {
            nativeQuery.setParameter("value", "%" + value + "%");
        }
        nativeQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        List<String> resultList = nativeQuery.getResultList();
        return new HashSet<>(resultList);
    }

    @Override
    public PageResult<OpMerchantListVO> searchOpMerchants(String value, Pageable pageable) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.username,u.enterprise_name,ao.org_name,ao.name,u.phone,count(DISTINCT wp.id),count(DISTINCT d.id) FROM" +
                " user u LEFT JOIN auth_org ao ON u.id=ao.user_id LEFT JOIN wash_point wp ON u.id=wp.merchant_id LEFT JOIN device d ON u.id=d.user_id WHERE EXISTS (" +
                " SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and (u.enterprise_name like :value or ao.name like :value or u.phone like :value)");
        }
        pageSqlBuilder.append("  GROUP BY u.id,u.username,u.enterprise_name,ao.org_name,ao.name,u.phone limit :start,:end");
        StringBuilder totalNumSqlBuilder = new StringBuilder("select count(u.id) from user u LEFT JOIN auth_org ao ON u.id=ao.user_id where EXISTS " +
                "(SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            totalNumSqlBuilder.append(" and (u.enterprise_name like :value or ao.name like :value or u.phone like :value)");
        }
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();
        List<OpMerchantListVO> opMerchantListVOS = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String username = (String) tuple[1];
            String enterpriseName = (String) tuple[2];
            String orgName = (String) tuple[3];
            String orgAuthName = (String) tuple[4];
            String phone = (String) tuple[5];
            Long washPointCount = ((BigInteger) tuple[6]).longValue();
            Long deviceCount = ((BigInteger) tuple[7]).longValue();
            OpMerchantListVO opMerchantListVO = new OpMerchantListVO();
            opMerchantListVO.setId(userId);
            opMerchantListVO.setUsername(username);
            opMerchantListVO.setEnterpriseName(enterpriseName);
            opMerchantListVO.setOrgName(orgName);
            opMerchantListVO.setAuthOrgName(orgAuthName);
            opMerchantListVO.setPhone(phone);
            opMerchantListVO.setWashPointCount(washPointCount);
            opMerchantListVO.setDeviceCount(deviceCount);
            return opMerchantListVO;
        }).collect(Collectors.toList());

        PageResult<OpMerchantListVO> pageResult = new PageResult<>();
        pageResult.setElements(opMerchantListVOS);

        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            totalNumQuery.setParameter("value", "%" + value + "%");
        }
        totalNumQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(totalNumQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        pageResult.setTotalSize(count.longValue());
        return pageResult;
    }

    @Override
    public PageResult<FIListVO> searchFIs(String keyword, Pageable pageable) {
        StringBuilder pageSqlBuilder = new StringBuilder("select  u.id,u.enterprise_name,u.auth_status,sum(l.loan_amount)" +
                " from user u left join loan l on u.id=l.lender_id where  EXISTS (" +
                "SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!StringUtils.isBlank(keyword)) {
            pageSqlBuilder.append(" and u.enterprise_name like :keyword");
        }
        pageSqlBuilder.append(" group by u.id,u.enterprise_name,u.auth_status order by u.enterprise_name");
        pageSqlBuilder.append(" limit :start,:end");
        StringBuilder countSqlBuilder = new StringBuilder("select count(u.id) from user u where  EXISTS (" +
                "SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!StringUtils.isBlank(keyword)) {
            countSqlBuilder.append(" and u.enterprise_name like :keyword");
        }
        Query countQuery = entityManager.createNativeQuery(countSqlBuilder.toString());
        countQuery.setParameter("roleType", RoleType.FINANCIAL_INSTITUTIONS.name());
        if (!StringUtils.isBlank(keyword)) {
            countQuery.setParameter("keyword", "%" + keyword + "%");
        }
        Long userCount = Optional.ofNullable((BigInteger) QueryUtil.getSingleResult(countQuery)).orElse(BigInteger.ZERO).longValue();

        Query query = entityManager.createNativeQuery(pageSqlBuilder.toString());
        query.setParameter("roleType", RoleType.FINANCIAL_INSTITUTIONS.name());
        if (!StringUtils.isBlank(keyword)) {
            query.setParameter("keyword", "%" + keyword + "%");
        }
        query.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        query.setParameter("end", pageable.getPageSize());
        List<Object[]> resultList = query.getResultList();

        Map<String, BigInteger> userIdAmountMap = this.searchNeedAuditLoanCount(keyword, pageable);
        List<FIListVO> fiListVOList = resultList.stream().map(tuple -> {
            String id = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Integer authStatus = (Integer) tuple[2];
            BigDecimal issuedLoanAmount = (BigDecimal) tuple[3];
            FIListVO fiListVO = new FIListVO();
            fiListVO.setId(id);
            fiListVO.setEnterpriseName(enterpriseName);
            fiListVO.setAuthStatus(authStatus);
            fiListVO.setIssuedLoanAmount(NumberUtil.roundStr(Optional.ofNullable(issuedLoanAmount).orElse(BigDecimal.ZERO).doubleValue(), 0));
            fiListVO.setNeedAuditLoanCount(Optional.ofNullable(userIdAmountMap.get(id)).orElse(BigInteger.ZERO).longValue());
            return fiListVO;
        }).collect(Collectors.toList());

        return new PageResult<>(userCount, fiListVOList);
    }

    private Map<String, BigInteger> searchNeedAuditLoanCount(String keyword, Pageable pageable) {
        StringBuilder sqlBuilder = new StringBuilder("select u.id,count(l.id) from user u left join loan l on u.id=l.lender_id" +
                " where  EXISTS ( SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)" +
                " and l.status=:loanStatus");
        if (!StringUtils.isBlank(keyword)) {
            sqlBuilder.append(" and u.enterprise_name like :keyword");
        }
        sqlBuilder.append(" group by u.id,u.enterprise_name,u.auth_status order by u.enterprise_name");
        if (null != pageable) {
            sqlBuilder.append(" limit :start,:end");
        }
        Query nativeQuery = entityManager.createNativeQuery(sqlBuilder.toString());

        nativeQuery.setParameter("roleType", RoleType.FINANCIAL_INSTITUTIONS.name());
        nativeQuery.setParameter("loanStatus", LoanStatus.AUDITING);
        if (!StringUtils.isBlank(keyword)) {
            nativeQuery.setParameter("keyword", "%" + keyword + "%");
        }
        if (null != pageable) {
            nativeQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
            nativeQuery.setParameter("end", pageable.getPageSize());
        }
        List<Object[]> resultList = nativeQuery.getResultList();
        return resultList.stream().collect(Collectors.toMap(tuple -> (String) tuple[0], tuple -> (BigInteger) tuple[1]));
    }

    @Override
    public List<BorrowerExportVO> exportBorrower(String value) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT l.borrower_id,u.enterprise_name,count(DISTINCT d.id)," +
                "count(DISTINCT p.id) FROM loan l JOIN user u ON l.borrower_id=u.id LEFT JOIN project p on l.id=p.loan_id" +
                " LEFT JOIN device d on p.id=d.project_id where l.lender_id=:lenderId and l.status!='CREATING'");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and u.enterprise_name like :value");
        }
        pageSqlBuilder.append(" GROUP BY l.borrower_id,u.enterprise_name");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("lenderId", AuthenticationHolder.getUserId());
        List<Object[]> resultList = pageQuery.getResultList();
        List<BorrowerExportVO> exportVOList = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Long deviceCount = ((BigInteger) tuple[2]).longValue();
            Long projectCount = ((BigInteger) tuple[3]).longValue();
            BorrowerExportVO borrowerExportVO = new BorrowerExportVO();
            borrowerExportVO.setBorrowerId(userId);
            borrowerExportVO.setEnterpriseName(enterpriseName);
            borrowerExportVO.setDeviceCount(deviceCount);
            borrowerExportVO.setProjectCount(projectCount);
            return borrowerExportVO;
        }).collect(Collectors.toList());
        Set<String> projectId = this.searchBorrowerProjectIds(value);
        Map<String, String> userIdOrderTotalMap = deviceOrderService.getUserIdOrderTotalMap(projectId);
        exportVOList.forEach(exportVO -> {
            exportVO.setOperationAmount(userIdOrderTotalMap.get(exportVO.getBorrowerId()));
        });
        return exportVOList;
    }

    @Override
    public List<OMExportVO> exportOM(List<String> ids, String value) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.enterprise_name,u.auth_status,count(DISTINCT d.id)," +
                "count(DISTINCT p.id) FROM user u LEFT JOIN device d ON u.id=d.user_id LEFT JOIN project p ON u.id=p.user_id where EXISTS (" +
                "SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!CollectionUtils.isEmpty(ids)) {
            pageSqlBuilder.append(" and u.id in (:ids)");
        }
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and u.enterprise_name like :value");
        }
        pageSqlBuilder.append(" GROUP BY u.id,u.enterprise_name,u.auth_status order by u.enterprise_name");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (!CollectionUtils.isEmpty(ids)) {
            pageQuery.setParameter("ids", ids);
        }
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        List<Object[]> resultList = pageQuery.getResultList();
        List<OMExportVO> exportVOList = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Integer authStatus = (Integer) tuple[2];
            Long deviceCount = ((BigInteger) tuple[3]).longValue();
            Long projectCount = ((BigInteger) tuple[4]).longValue();
            OMExportVO omExportVO = new OMExportVO();
            omExportVO.setBorrowerId(userId);
            omExportVO.setEnterpriseName(enterpriseName);
            AuthStatus status = AuthStatus.fromCode(authStatus);
            if (null != status) {
                omExportVO.setAuthStatus(status.getStatus());
            }
            omExportVO.setDeviceCount(deviceCount);
            omExportVO.setProjectCount(projectCount);
            return omExportVO;
        }).collect(Collectors.toList());
        Set<String> projectId = this.searchOMProjectIds(value);
        Map<String, String> userIdOrderTotalMap = deviceOrderService.getUserIdOrderTotalMap(projectId);
        exportVOList.forEach(exportVO -> {
            exportVO.setOperationAmount(userIdOrderTotalMap.get(exportVO.getBorrowerId()));
        });
        return exportVOList;
    }

    @Override
    public List<OpMerchantExportVO> exportOpMerchant(List<String> ids, String value) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.enterprise_name,ao.name,u.phone,count(DISTINCT wp.id),count(DISTINCT d.id) FROM" +
                " user u LEFT JOIN auth_org ao ON u.id=ao.user_id LEFT JOIN wash_point wp ON u.id=wp.merchant_id LEFT JOIN device d ON u.id=d.user_id WHERE EXISTS (" +
                " SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!CollectionUtils.isEmpty(ids)) {
            pageSqlBuilder.append(" and u.id in (:ids)");
        }
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and (u.enterprise_name like :value or ao.name like :value or u.phone like :value)");
        }
        pageSqlBuilder.append("  GROUP BY u.id,u.username,u.enterprise_name,ao.NAME,u.phone");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (!CollectionUtils.isEmpty(ids)) {
            pageQuery.setParameter("ids", ids);
        }
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("roleType", RoleType.OPERATIONAL_MERCHANT.name());
        List<Object[]> resultList = pageQuery.getResultList();
        List<OpMerchantExportVO> opMerchantListVOS = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            String authOrgName = (String) tuple[2];
            String phone = (String) tuple[3];
            Long washPointCount = ((BigInteger) tuple[4]).longValue();
            Long deviceCount = ((BigInteger) tuple[5]).longValue();
            OpMerchantExportVO opMerchantExportVO = new OpMerchantExportVO();
            opMerchantExportVO.setId(userId);
            opMerchantExportVO.setEnterpriseName(enterpriseName);
            opMerchantExportVO.setAuthOrgName(authOrgName);
            opMerchantExportVO.setPhone(phone);
            opMerchantExportVO.setWashPointCount(washPointCount);
            opMerchantExportVO.setDeviceCount(deviceCount);
            return opMerchantExportVO;
        }).collect(Collectors.toList());
        return opMerchantListVOS;
    }

    @Override
    public List<PMExportVO> exportPM(List<String> ids, String value) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.enterprise_name,u.auth_status,count(DISTINCT d.id)" +
                " FROM user u LEFT JOIN device d ON u.id=d.user_id where EXISTS (SELECT ur.user_id FROM user_role ur JOIN" +
                " role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!CollectionUtils.isEmpty(ids)) {
            pageSqlBuilder.append(" and u.id in (:ids)");
        }
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and enterprise_name like :keyword");
        }
        pageSqlBuilder.append(" GROUP BY u.id,u.enterprise_name,u.auth_status order by u.enterprise_name");
        Query query = entityManager.createNativeQuery(pageSqlBuilder.toString());
        query.setParameter("roleType", RoleType.PLATFORM_MERCHANT.name());
        if (!CollectionUtils.isEmpty(ids)) {
            query.setParameter("ids", ids);
        }
        if (!StringUtils.isBlank(value)) {
            query.setParameter("keyword", "%" + value + "%");
        }
        List<Object[]> resultList = query.getResultList();
        return resultList.stream()
                .map(tuple -> {
                    String userId = (String) tuple[0];
                    String enterpriseName = (String) tuple[1];
                    Integer authStatus = (Integer) tuple[2];
                    Long deviceCount = ((BigInteger) tuple[3]).longValue();
                    PMExportVO pmExportVO = new PMExportVO();
                    pmExportVO.setBorrowerId(userId);
                    pmExportVO.setEnterpriseName(enterpriseName);
                    AuthStatus status = AuthStatus.fromCode(authStatus);
                    if (null != status) {
                        pmExportVO.setAuthStatus(status.getStatus());
                    }

                    pmExportVO.setDeviceCount(deviceCount);
                    return pmExportVO;
                }).collect(Collectors.toList());
    }

    @Override
    public List<FIExportVO> exportFI(List<String> ids, String keyword) {
        StringBuilder pageSqlBuilder = new StringBuilder("select  u.id,u.enterprise_name,u.auth_status,sum(l.loan_amount)" +
                " from user u left join loan l on u.id=l.lender_id where  EXISTS (" +
                "SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!CollectionUtils.isEmpty(ids)) {
            pageSqlBuilder.append(" and u.id in (:ids)");
        }
        if (!StringUtils.isBlank(keyword)) {
            pageSqlBuilder.append(" and u.enterprise_name like :keyword");
        }
        pageSqlBuilder.append(" group by u.id,u.enterprise_name,u.auth_status order by u.enterprise_name");

        Query query = entityManager.createNativeQuery(pageSqlBuilder.toString());
        query.setParameter("roleType", RoleType.FINANCIAL_INSTITUTIONS.name());
        if (!CollectionUtils.isEmpty(ids)) {
            query.setParameter("ids", ids);
        }
        if (!StringUtils.isBlank(keyword)) {
            query.setParameter("keyword", "%" + keyword + "%");
        }
        List<Object[]> resultList = query.getResultList();

        Map<String, BigInteger> userIdAmountMap = this.searchNeedAuditLoanCount(keyword, null);
        return resultList.stream().map(tuple -> {
            String id = (String) tuple[0];
            String enterpriseName = (String) tuple[1];
            Integer authStatus = (Integer) tuple[2];
            BigDecimal issuedLoanAmount = (BigDecimal) tuple[3];
            FIExportVO fiExportVO = new FIExportVO();
            fiExportVO.setId(id);
            fiExportVO.setEnterpriseName(enterpriseName);
            AuthStatus status = AuthStatus.fromCode(authStatus);
            if (null != status) {
                fiExportVO.setAuthStatus(status.getStatus());
            }
            fiExportVO.setIssuedLoanAmount(Optional.ofNullable(issuedLoanAmount).orElse(BigDecimal.ZERO).doubleValue());
            fiExportVO.setNeedAuditLoanCount(Optional.ofNullable(userIdAmountMap.get(id)).orElse(BigInteger.ZERO).longValue());
            return fiExportVO;
        }).collect(Collectors.toList());
    }

    @Override
    public PageResult<PlatEnterpriseListVO> getPlatEnterpriseList(Pageable pageable, String keyword) {
        StringBuffer enterpriseSql =
                new StringBuffer("select * from user where id in (SELECT ur.user_id from user_role ur " +
                        "JOIN role r on ur.role_id = r.id where r.type = :type)");
        StringBuffer countSql = new StringBuffer("select count(1) from user where id in (SELECT ur.user_id from user_role ur " +
                "JOIN role r on ur.role_id = r.id where r.type = :type)");
        if (!StringUtils.isBlank(keyword)) {
            enterpriseSql.append(" and enterprise_name like :keyword");
        }
        enterpriseSql.append(" limit :start,:end");
        if (!StringUtils.isBlank(keyword)) {
            countSql.append(" and enterprise_name like :keyword");
        }
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        countQuery.setParameter("type", RoleType.PLATFORM_MERCHANT.name());
        if (!StringUtils.isBlank(keyword)) {
            countQuery.setParameter("keyword", "%" + keyword + "%");
        }
        Long userCount = Optional.ofNullable((BigInteger) QueryUtil.getSingleResult(countQuery)).orElse(BigInteger.ZERO).longValue();

        Query query = entityManager.createNativeQuery(enterpriseSql.toString(), UserEntity.class);
        query.setParameter("type", RoleType.PLATFORM_MERCHANT.name());
        if (!StringUtils.isBlank(keyword)) {
            query.setParameter("keyword", keyword);
        }
        query.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        query.setParameter("end", pageable.getPageSize());
        List<UserEntity> userList = query.getResultList();
        List<PlatEnterpriseListVO> platEnterList = userList.stream().map(user -> {
            PlatEnterpriseListVO platEnterpriseListVO = new PlatEnterpriseListVO();
            platEnterpriseListVO.setBorrowerId(user.getId());
            platEnterpriseListVO.setAuthStatus(user.getAuthStatus());
            platEnterpriseListVO.setEnterpriseName(user.getEnterpriseName());
            String id = user.getId();
            String deviceSql = "select count(*) from device where user_id =:userId";
            Query deviceCountQuery = entityManager.createNativeQuery(deviceSql);
            deviceCountQuery.setParameter("userId", id);
            Long deviceCount = Optional.ofNullable((BigInteger) QueryUtil.getSingleResult(deviceCountQuery)).orElse(BigInteger.ZERO).longValue();
            platEnterpriseListVO.setDeviceCount(deviceCount);
            return platEnterpriseListVO;
        }).collect(Collectors.toList());

        PageResult pg = new PageResult(userCount, platEnterList);

        return pg;
    }

    @Override
    public MerchantFIDetailVO getPlatDetail(String borrowerId) {
        return getEnterpriseDetail(borrowerId, RoleType.PLATFORM_MERCHANT);
    }

    @Override
    public MerchantFIDetailVO fiDetail(String id) {
        return getEnterpriseDetail(id, RoleType.FINANCIAL_INSTITUTIONS);
    }

    @Override
    public List<BorrowHistoryExportVO> getBorrowerHistoryExportList(String keyword) {
        StringBuilder sb = new StringBuilder("select l.borrower_id,u.enterprise_name,count(distinct p.id),sum(l.issue_amount) from loan l" +
                " JOIN user u on l.borrower_id=u.id join project p on p.loan_id=l.id WHERE l.lender_id=:lenderId and not exists(" +
                "select p.id from project p where p.id=l.id and p.status='BUSINESS' and p.status='PREPARE')");
        if (!StringUtils.isBlank(keyword)) {
            sb.append(" and u.enterprise_name like :keyword ");
        }
        sb.append(" GROUP BY l.borrower_id,u.enterprise_name");
        Query query = entityManager.createNativeQuery(sb.toString());
        query.setParameter("lenderId", AuthenticationHolder.getUserId());
        if (!StringUtils.isBlank(keyword)) {
            query.setParameter("keyword", "%" + keyword + "%");
        }

        List<Object[]> resultList = query.getResultList();
        List<BorrowHistoryExportVO> result = resultList.stream()
                .map(x -> {
                    String enterpriseName = (String) x[1];
                    BigInteger projectCount = (BigInteger) x[2];
                    if (null == projectCount) {
                        projectCount = BigInteger.ZERO;
                    }
                    BigDecimal actualProvideAmount = (BigDecimal) x[3];
                    if (actualProvideAmount == null) {
                        actualProvideAmount = BigDecimal.ZERO;
                    }
                    return BorrowHistoryExportVO.builder()
                            .enterpriseName(enterpriseName)
                            .projectCount(projectCount.longValue())
                            .actualProvideAmount(actualProvideAmount.doubleValue())
                            .build();
                }).collect(Collectors.toList());
        return result;
    }

    @Override
    public BorrowerHistoryDetailVO getBorrowHistoryDetail(String borrowerId) {
        StringBuilder sb = new StringBuilder("select l.borrower_id, u.enterprise_name, r.type" +
                ", u.auth_status, u.address, sum(l.actual_amount)" +
                " from loan l LEFT JOIN user u on l.borrower_id = u.id LEFT JOIN user_role ur on u.id = ur.user_id" +
                " left JOIN role r on ur.role_id = r.id where l.borrower_id=:borrowerId" +
                " GROUP BY l.borrower_id,u.enterprise_name,r.type");

        Query query = entityManager.createNativeQuery(sb.toString());
        query.setParameter("borrowerId", borrowerId);
        Object[] singleResult = (Object[]) Optional.ofNullable(query.getSingleResult()).orElseThrow(() -> new BusinessException(Code.ERROR, "没有找到对应运营商"));

        return BorrowerHistoryDetailVO.builder().id((String) singleResult[0]).enterpriseName((String) singleResult[1])
                .enterpriseType(RoleType.getDescByName((String) singleResult[2])).authStatus(AccountStatus.getStatusByCode((Integer) singleResult[3]))
                .detailAddr((String) singleResult[4]).totalLoanAmount((Double) singleResult[5]).build();
    }

    private MerchantFIDetailVO getEnterpriseDetail(String borrowerId, RoleType type) {
        UserEntity userEntity = userEntityRepo.findById(borrowerId).orElse(null);
        if (null == userEntity) {
            return null;
        }
        MerchantFIDetailVO omDetailVO = new MerchantFIDetailVO();
        omDetailVO.setId(userEntity.getId());
        omDetailVO.setAuthStatus(userEntity.getAuthStatus());
        omDetailVO.setEnterpriseName(userEntity.getEnterpriseName());
        omDetailVO.setEnterpriseType(type.name());
        omDetailVO.setPhone(userEntity.getPhone());
        AuthOrgEntity authOrgEntity = authOrgEntityRepo.findTopByUserIdOrderByCreateTimeDesc(userEntity.getId());
        if (null != authOrgEntity) {
            omDetailVO.setDetailAddr(authOrgEntity.getDetailAddr());
            omDetailVO.setOrgName(authOrgEntity.getOrgName());
            List<AttachVO> attachVOList = attachmentService.getAttachment(authOrgEntity.getId(), Arrays.asList(AttachType.LICENSE));
            if (CollectionUtil.isNotEmpty(attachVOList)) {
                omDetailVO.setLicenseUrl(attachVOList.get(0).getUrl());
            }
        }
        return omDetailVO;
    }

    /**
     * 重置密码
     *
     * @param password
     * @param passwordConfirm
     * @param userAuthEntity
     * @return
     */
    private BaseResult<?> resetPassword(String password, String passwordConfirm, UserAuthEntity userAuthEntity) {
        if (null == userAuthEntity) {
            return BaseResultFactory.produceEmptyResult(Code.INVALID_USER);
        }
        // 判断两次密码是否一致
        if (!password.equals(passwordConfirm)) {
            return BaseResultFactory.produceEmptyResult(Code.PASSWORD_DIFF);
        }
        userAuthEntity.setCredential(EncryptUtil.encodePassword(password));
        userAuthService.updateByIdentifier(userAuthEntity);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    @Transactional
    public void activate(AccountActivateDTO accountActivateDTO) {
        String userId = AuthenticationHolder.getUserId();
        UserEntity userEntity = userEntityRepo.findById(userId).orElseThrow(() -> new BusinessException(Code.NODATA, "用户不存在"));
        if (userEntity.getActivateStatus() == AccountActiveStatus.ACTIVATED) {
            throw new BusinessException(Code.ERROR, "账户已激活");
        }
        userEntity.setActivateStatus(AccountActiveStatus.ACTIVATED);
        userEntityRepo.save(userEntity);
        ResetPasswordByCodeVO resetPasswordByCodeVO = new ResetPasswordByCodeVO();
        resetPasswordByCodeVO.setPhone(userEntity.getPhone());
        resetPasswordByCodeVO.setVerifyCode(accountActivateDTO.getVerifyCode());
        resetPasswordByCodeVO.setPassword(accountActivateDTO.getPassword());
        resetPasswordByCodeVO.setPasswordConfirm(accountActivateDTO.getPassword());
        this.resetPasswordByVerifyCode(resetPasswordByCodeVO);
    }

    @Override
    public PageResult<CustomerListVO> searchCustomer(String value, Pageable pageable) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.username,u.phone,u.create_time,sum(dor.price) FROM" +
                " user u LEFT JOIN device_order dor ON u.id=dor.customer_id WHERE EXISTS (SELECT ur.user_id FROM user_role ur" +
                "  JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            pageSqlBuilder.append(" and (u.phone like :value or u.username like :value)");
        }
        pageSqlBuilder.append(" GROUP BY u.id,u.username,u.phone,u.create_time limit :start,:end");
        StringBuilder totalNumSqlBuilder = new StringBuilder("select count(u.id) from user u where EXISTS " +
                "(SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (StringUtils.isNotBlank(value)) {
            totalNumSqlBuilder.append(" and (u.phone like :value or u.username like :value)");
        }
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            pageQuery.setParameter("value", "%" + value + "%");
        }
        pageQuery.setParameter("roleType", RoleType.CUSTOMER.name());
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();
        List<CustomerListVO> customerListVOList = resultList.stream().map(tuple -> {
            String userId = (String) tuple[0];
            String username = (String) tuple[1];
            String phone = (String) tuple[2];
            String createTime = ((String) tuple[3]);
            BigDecimal amount = ((BigDecimal) tuple[4]);
            CustomerListVO customerListVO = new CustomerListVO();
            customerListVO.setId(userId);
            customerListVO.setPhone(phone);
            customerListVO.setUsername(username);
            customerListVO.setAmount(AmountUtil.amountRoundStr(amount, 2));
            customerListVO.setCreateTime(createTime);
            return customerListVO;
        }).collect(Collectors.toList());
        PageResult<CustomerListVO> pageResult = new PageResult<>();
        pageResult.setElements(customerListVOList);

        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (StringUtils.isNotBlank(value)) {
            totalNumQuery.setParameter("value", "%" + value + "%");
        }
        totalNumQuery.setParameter("roleType", RoleType.CUSTOMER.name());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(totalNumQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        pageResult.setTotalSize(count.longValue());
        return pageResult;
    }

    @Override
    public List<CustomerExportVO> exportCustomer(List<String> ids, String value) {
        StringBuilder sqlBuilder = new StringBuilder("SELECT u.id,u.username,u.phone,u.create_time,sum(dor.price) FROM" +
                " user u LEFT JOIN device_order dor ON u.id=dor.customer_id WHERE EXISTS (SELECT ur.user_id FROM user_role ur" +
                "  JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        if (!CollectionUtils.isEmpty(ids)) {
            sqlBuilder.append(" and u.id in (:ids)");
        }
        if (StringUtils.isNotBlank(value)) {
            sqlBuilder.append(" and (u.phone like :value or u.username like :value)");
        }
        sqlBuilder.append(" GROUP BY u.id,u.username,u.phone,u.create_time");
        Query query = entityManager.createNativeQuery(sqlBuilder.toString());
        // 填充参数
        if (!CollectionUtils.isEmpty(ids)) {
            query.setParameter("ids", ids);
        }
        if (StringUtils.isNotBlank(value)) {
            query.setParameter("value", "%" + value + "%");
        }
        query.setParameter("roleType", RoleType.CUSTOMER.name());

        List<Object[]> resultList = query.getResultList();
        List<CustomerExportVO> result = resultList.stream()
                .map(tuple -> {
                    String userId = (String) tuple[0];
                    String username = (String) tuple[1];
                    String phone = (String) tuple[2];
                    String createTime = ((String) tuple[3]);
                    BigDecimal amount = ((BigDecimal) tuple[4]);
                    CustomerExportVO customerExportVO = new CustomerExportVO();
                    customerExportVO.setId(userId);
                    customerExportVO.setPhone(phone);
                    customerExportVO.setAmount(AmountUtil.amountRoundStr(amount, 2));
                    customerExportVO.setCreateTime(new Date(Long.parseLong(createTime)));
                    return customerExportVO;
                }).collect(Collectors.toList());
        return result;
    }

    @Override
    public PageResult<AccountListVO> searchAccount(Pageable pageable, AccountSearchDTO accountSearchDTO) {
        StringBuilder pageSqlBuilder = new StringBuilder("SELECT u.id,u.username,u.description,r.name,u.phone,u.merchant_id," +
                "u.merchant_name,u.creator_id,u.creator_name,u.create_time,u.enable,r.id as rid FROM user u LEFT JOIN user_role ur ON ur.user_id=u.id" +
                " LEFT JOIN role r ON ur.role_id=r.id WHERE (r.type!='CUSTOMER' or r.type is null)");
        if (AuthenticationHolder.isAdmin()) {
            pageSqlBuilder.append(" AND u.id!=:userId");
        } else {
            pageSqlBuilder.append(" AND u.id in (:userIds)");
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getRoleId())) {
            pageSqlBuilder.append(" AND r.id=:roleId ");
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getKeyword())) {
            pageSqlBuilder.append(" AND (u.username LIKE :keyword OR u.description LIKE :keyword OR u.phone LIKE :keyword" +
                    " OR u.merchant_name LIKE :keyword)");
        }
        pageSqlBuilder.append(" order by u.create_time desc");
        pageSqlBuilder.append(" limit :start,:end");
        Query pageQuery = entityManager.createNativeQuery(pageSqlBuilder.toString());
        // 填充参数
        if (AuthenticationHolder.isAdmin()) {
            pageQuery.setParameter("userId", AuthenticationHolder.getUserId());
        } else {
            Set<String> descendantIds = userRelService.getDescendantIds(AuthenticationHolder.getUserId());
            if (CollectionUtils.isEmpty(descendantIds)) {
                descendantIds.add("-1");
            }
            pageQuery.setParameter("userIds", descendantIds);
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getRoleId())) {
            pageQuery.setParameter("roleId", accountSearchDTO.getRoleId());
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getKeyword())) {
            pageQuery.setParameter("keyword", "%" + accountSearchDTO.getKeyword() + "%");
        }
        pageQuery.setParameter("start", pageable.getPageSize() * pageable.getPageNumber());
        pageQuery.setParameter("end", pageable.getPageSize());

        List<Object[]> resultList = pageQuery.getResultList();
        List<AccountListVO> accountListVOList = resultList.stream()
                .map(tuple -> {
                    String userId = (String) tuple[0];
                    String username = (String) tuple[1];
                    String desc = (String) tuple[2];
                    String roleName = ((String) tuple[3]);
                    String phone = ((String) tuple[4]);
                    String merchantId = ((String) tuple[5]);
                    String merchantName = ((String) tuple[6]);
                    String creatorId = ((String) tuple[7]);
                    String creatorName = ((String) tuple[8]);
                    String createTime = ((String) tuple[9]);
                    Boolean enable = ((Boolean) tuple[10]);
                    String roleId = ((String) tuple[11]);
                    AccountListVO accountListVO = new AccountListVO();
                    accountListVO.setId(userId);
                    accountListVO.setUsername(username);
                    accountListVO.setDescription(desc);
                    accountListVO.setRoleName(roleName);
                    accountListVO.setPhone(phone);
                    accountListVO.setMerchantId(merchantId);
                    accountListVO.setMerchantName(merchantName);
                    accountListVO.setCreatorId(creatorId);
                    accountListVO.setCreatorName(creatorName);
                    accountListVO.setUsername(username);
                    accountListVO.setCreateTime(createTime);
                    accountListVO.setEnable(enable);
                    accountListVO.setRoleId(roleId);
                    return accountListVO;
                }).collect(Collectors.toList());
        PageResult<AccountListVO> pageResult = new PageResult<>();
        pageResult.setElements(accountListVOList);

        StringBuilder totalNumSqlBuilder = new StringBuilder("SELECT count(*) FROM user u LEFT JOIN user_role ur ON ur.user_id=u.id" +
                " LEFT JOIN role r ON ur.role_id=r.id WHERE  (r.type!='CUSTOMER' or r.type is null)");
        if (AuthenticationHolder.isAdmin()) {
            totalNumSqlBuilder.append(" AND u.id!=:userId");
        } else {
            totalNumSqlBuilder.append(" AND u.id in (:userIds)");
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getRoleId())) {
            totalNumSqlBuilder.append(" AND r.id=:roleId ");
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getKeyword())) {
            totalNumSqlBuilder.append(" AND (u.username LIKE :keyword OR u.description LIKE :keyword OR u.phone LIKE :keyword" +
                    " OR u.merchant_name LIKE :keyword)");
        }
        Query totalNumQuery = entityManager.createNativeQuery(totalNumSqlBuilder.toString());
        // 填充参数
        if (AuthenticationHolder.isAdmin()) {
            totalNumQuery.setParameter("userId", AuthenticationHolder.getUserId());
        } else {
            Set<String> descendantIds = userRelService.getDescendantIds(AuthenticationHolder.getUserId());
            if (CollectionUtils.isEmpty(descendantIds)) {
                descendantIds.add("-1");
            }
            totalNumQuery.setParameter("userIds", descendantIds);
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getRoleId())) {
            totalNumQuery.setParameter("roleId", accountSearchDTO.getRoleId());
        }
        if (StringUtils.isNotBlank(accountSearchDTO.getKeyword())) {
            totalNumQuery.setParameter("keyword", "%" + accountSearchDTO.getKeyword() + "%");
        }
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(totalNumQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        pageResult.setTotalSize(count.longValue());
        return pageResult;
    }

    @Override
    @Transactional
    public BaseResult<?> createAccount(AccountCreateDTO createDTO) {
        // 判断用户是否已经存在
        UserEntity userEntity = userEntityRepo.findbyPhone(createDTO.getPhone());
        String creatorId = AuthenticationHolder.getUserId();
        if (ObjectUtils.isEmpty(userEntity)) {
//            return BaseResultFactory.produceEmptyResult(Code.PHONE_EXIST);
            userEntity = new UserEntity();
        } else {
            if (StringUtils.equals(creatorId,userEntity.getId())) {
                return BaseResultFactory.produceEmptyResult(Code.ADD_SELF_AS_SUB);
            }
            if (StringUtils.isNotBlank(userEntity.getCreatorId())) {
                return BaseResultFactory.produceEmptyResult(Code.ADD_REPEAT_SUB);
            }
        }
        String roleId = createDTO.getRoleId();
        RoleEntity roleEntity = roleService.getById(roleId).orElseThrow(() -> new BusinessException(Code.NODATA));
        List<AuthorityEntity> authorities = authorityService.getAuthorities(roleEntity.getId());
        if (CollectionUtils.isEmpty(authorities)) {
            return BaseResultFactory.produceEmptyResult(Code.ROLE_AUTHORITY_IS_MISSING);
        }

        // 存储用户信息
        userEntity.setPhone(createDTO.getPhone());
        userEntity.setStatus(AccountStatus.VALID.getCode());
        userEntity.setActivateStatus(AccountActiveStatus.WAIT_ACTIVATED);
        //long count = userEntityRepo.count();
        //userEntity.setUsername("用户" + StringUtils.leftPad(String.valueOf(count), 5, "0"));
        userEntity.setUsername(createDTO.getUsername());
        userEntity.setAuthStatus(AuthStatus.UNAUTH.getCode());
        userEntity.setErrorCount(0);
        userEntity.setEnable(true);
        userEntity.setDescription(createDTO.getDescription());
        boolean createMerchant = AuthenticationHolder.isAdmin() && RoleType.OPERATIONAL_MERCHANT.name().equals(roleEntity.getType());
        if (createMerchant) {
            // 创建运营商
            userEntityRepo.save(userEntity);
            // 在所属运营商下面创建默认的角色
            List<RoleCreateDTO> roleCreateDTOS = buildDefaultMerchantRoles();
            roleCreateDTOS.forEach(r -> {
                roleService.create(r);
            });
        } else {
            // 创建子账号
            UserEntity merchant = userRelService.getUserMerchant(AuthenticationHolder.getUserId());
            userEntity.setMerchantId(merchant.getId());
            userEntity.setMerchantName(merchant.getEnterpriseName());
            userEntityRepo.save(userEntity);
            // 创建用户关系
            UserRelEntity userRelEntity = new UserRelEntity();
            userRelEntity.setParentId(creatorId);
            userRelEntity.setUserId(userEntity.getId());
            userRelEntity.setMerchantId(merchant.getId());
            userRelService.create(userRelEntity);
        }

        // 存储认证信息
        UserAuthDTO userAuthDTO = new UserAuthDTO();
        userAuthDTO.setUserId(userEntity.getId());
        userAuthDTO.setIdentityType(IdentityType.PHONE);
        userAuthDTO.setIdentifier(userEntity.getPhone());
        userAuthDTO.setCredential(EncryptUtil.encodePassword(createDTO.getPhone()));
        userAuthService.create(userAuthDTO);
        // 绑定角色
        roleService.bindRoles(userEntity.getId(), Lists.newArrayList(roleId));
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    /**
     * 构建默认的运营商所拥有的角色
     * @return
     */
    private List<RoleCreateDTO> buildDefaultMerchantRoles(){
        List<RoleCreateDTO> list = Lists.newArrayList();
        // 不管运维还是财务，统称为运维人员 SlaveRoleIds:不明确这个角色的用意，可能和子商户有关
        // 洗衣点 设备 供应 权限 营销 交易的权限
        list.add(new RoleCreateDTO().setName("运维").setSlaveRoleIds(Arrays.asList("1")).setRoleType(RoleType.REPLENISH.name()).setDescription("运维人员").setAuthorityIds(Lists.newArrayList("103","104","105","2","3","4","5","6","7","9","8","18","24","11","12","13","19","20","23","25","26","27","37","36","40","41","42","43","44","45","48","49","50","51","53","52","57","110","56")));
        // 首页 交易 报表 财务的权限
        list.add(new RoleCreateDTO().setName("财务").setSlaveRoleIds(Arrays.asList("1")).setRoleType(RoleType.REPLENISH.name()).setDescription("财务人员").setAuthorityIds(Lists.newArrayList("1","31","58","62","56","57","110","59","60","61","63")));
        return list;
    }

    @Override
    @Transactional
    public BaseResult<?> modifyAccount(AccountModifyDTO modifyDTO) {
        String id = modifyDTO.getId();
        UserEntity userEntity = userEntityRepo.findById(id).orElseThrow(() -> new BusinessException(Code.NODATA));
        // 商户只能编辑本商户下的账号
        if (AuthenticationHolder.isMerchant() && !userEntity.getMerchantId().equals(AuthenticationHolder.getUserId())) {
            throw new BusinessException(Code.APP_PERMISSION_DENIED);
        }
        if (StringUtils.isNotBlank(modifyDTO.getPhone()) && !StringUtils.equals(modifyDTO.getPhone(), userEntity.getPhone())) {
            // 如果要修改手机号
            UserEntity userByPhone = userEntityRepo.findbyPhone(modifyDTO.getPhone());
            if (!ObjectUtils.isEmpty(userByPhone)) {
                return BaseResultFactory.produceEmptyResult(Code.PHONE_EXIST);
            }
            userEntity.setActivateStatus(AccountActiveStatus.WAIT_ACTIVATED);
            this.afterPhoneModified(userEntity.getId(), modifyDTO.getPhone());
        }
        BeanUtil.copyProperties(modifyDTO, userEntity, CopyOptions.create().setIgnoreNullValue(true));
        userEntityRepo.save(userEntity);
        if (StringUtils.isNotBlank(modifyDTO.getRoleId())) {
            userRoleEntityRepo.deleteByUserId(userEntity.getId());
            roleService.bindRoles(id, Lists.newArrayList(modifyDTO.getRoleId()));
        }
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    private void afterPhoneModified(String userId, String newPhone) {
        UserAuthDTO userAuthDTO = new UserAuthDTO();
        userAuthDTO.setUserId(userId);
        userAuthDTO.setIdentityType(IdentityType.PHONE);
        userAuthDTO.setIdentifier(newPhone);
        userAuthDTO.setCredential(EncryptUtil.encodePassword(newPhone));
        userAuthService.updateByIdentifier(userAuthDTO);
    }

    @Override
    @Transactional
    public BaseResult<?> delete(String id) {
        UserEntity userEntity = userEntityRepo.findById(id).orElseThrow(() -> new BusinessException(Code.NODATA));
        // 商户只能删除本商户下的账号
        if (AuthenticationHolder.isMerchant() && !userEntity.getMerchantId().equals(AuthenticationHolder.getUserId())) {
            throw new BusinessException(Code.APP_PERMISSION_DENIED);
        }
        // 登陆信息
        userAuthService.deleteByUserId(id);
        // 解绑角色
        roleService.unbindRoles(id);
        // 账号关系
        userRelService.deleteByUserId(id);
        // 认证信息
        authService.deleteAuthOrg(id);
        // 创建人
        //this.afterCreatorDeleted(id);

        userEntityRepo.delete(userEntity);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    public BaseResult<?> enable(String id, Boolean enable) {
        UserEntity userEntity = userEntityRepo.findById(id).orElseThrow(() -> new BusinessException(Code.NODATA));
        // 商户只能启用禁用本商户下的账号
        if (AuthenticationHolder.isMerchant() && !userEntity.getMerchantId().equals(AuthenticationHolder.getUserId())) {
            throw new BusinessException(Code.APP_PERMISSION_DENIED);
        }
        userEntity.setEnable(enable);
        userEntityRepo.save(userEntity);
        return BaseResultFactory.produceEmptyResult(Code.SUCCESS);
    }

    @Override
    public UserDetailVO detail(String id) {
        UserEntity userEntity = userEntityRepo.findById(id).orElseThrow(() -> new BusinessException(Code.NODATA, "用户不存在"));
        UserDetailVO userVO = new UserDetailVO();
        BeanUtil.copyProperties(userEntity, userVO);
        // 角色
        Set<String> roleTypes = roleService.getRoleIds(id);
        userVO.setRoleIds(roleTypes);
        return userVO;
    }

    @Override
    public List<UserSimpleVO> getSimpleList() {
        List<UserEntity> userEntities;
        if (AuthenticationHolder.isAdmin()) {
            // 管理员
            userEntities = userEntityRepo.findAllByEnable(true);
        } else {
            UserEntity merchant = userRelService.getUserMerchant(AuthenticationHolder.getUserId());
            // 能够查看所有数据
            if (AuthenticationHolder.getUserAuth().getViewAllData()) {
                userEntities = userEntityRepo.findAllByMerchantId(merchant.getId());
            } else {
                // 只能查看当前账号及其子账号下的数据
                Set<String> descendantIds = userRelService.getDescendantIds(AuthenticationHolder.getUserId());
                descendantIds.add(AuthenticationHolder.getUserId());
                userEntities = userEntityRepo.findAllByCreatorIdIn(descendantIds);
            }
        }
        return userEntities.stream().map(entity -> BeanCopyUtil.copy(entity, UserSimpleVO.class)).collect(Collectors.toList());
    }

    @Override
    public Long customerCount() {
        StringBuilder sqlBuilder = new StringBuilder("select count(u.id) from user u where EXISTS " +
                "(SELECT ur.user_id FROM user_role ur JOIN role r ON ur.role_id=r.id WHERE r.TYPE=:roleType AND ur.user_id=u.id)");
        Query countQuery = entityManager.createNativeQuery(sqlBuilder.toString());
        countQuery.setParameter("roleType", RoleType.CUSTOMER.name());
        BigInteger count = (BigInteger) QueryUtil.getSingleResult(countQuery);
        if (null == count) {
            count = BigInteger.ZERO;
        }
        return count.longValue();
    }

    @Override
    public void addSubClient(SubClientAddVO subClientAddVO) {
        if (userEntityRepo.findbyPhone(subClientAddVO.getPhone()) != null) {
            throw new BusinessException(Code.PHONE_EXIST);
        }

        if (userEntityRepo.findByEnterpriseName(subClientAddVO.getOrgName()) != null) {
            throw new BusinessException(Code.ENTERPRISE_NAME_EXISTS);
        }
        UserEntity userEntity = new UserEntity();
        userEntity.setPhone(subClientAddVO.getPhone());
        userEntity.setAuthStatus(AuthStatus.AUTH_SUCCESS.getCode());

        userEntity.setUsername(StringUtils.isBlank(subClientAddVO.getUsername()) ? subClientAddVO.getPhone()
                : subClientAddVO.getUsername());

        userEntity.setStatus(AccountStatus.VALID.getCode());
        userEntity.setActivateStatus(AccountActiveStatus.ACTIVATED);
        userEntity.setEnable(true);
        userEntity.setEnterpriseName(subClientAddVO.getOrgName());
        userEntity.setMerchantName(subClientAddVO.getOrgName());
        userEntity.setErrorCount(0);
        userEntity.setCreatorId(AuthenticationHolder.getUserId());
        userEntity.setCreatorName(AuthenticationHolder.getUserAuth().getUsername());
        userEntityRepo.save(userEntity);

        // 存储认证信息
        UserAuthDTO userAuthDTO = new UserAuthDTO();
        userAuthDTO.setUserId(userEntity.getId());
        userAuthDTO.setIdentityType(IdentityType.PHONE);
        userAuthDTO.setIdentifier(userEntity.getPhone());
        if (StringUtils.isBlank(subClientAddVO.getPassword())) {
            userAuthDTO.setCredential(EncryptUtil.encodePassword("16881688"));
        } else {
            userAuthDTO.setCredential(EncryptUtil.encodePassword(subClientAddVO.getPassword()));
        }
        userAuthService.create(userAuthDTO);
        // 绑定角色
        roleService.bindRoleTypes(userEntity.getId(), Arrays.asList(RoleType.OPERATIONAL_MERCHANT));
    }

    @Override
    public PageResult<SubClientListVO> getSubClientList(Pageable pageable, String keyword) {
        Page<UserEntity> result = userEntityRepo.findAll(assembleSubClientQueryCondition(keyword), pageable);
        return new PageResult(result.getTotalElements(), result.getContent().stream().map(x -> {
            SubClientListVO subClientListVO = new SubClientListVO();
            subClientListVO.setUserId(x.getId());
            subClientListVO.setPhone(x.getPhone());
            subClientListVO.setUsername(x.getUsername());
            subClientListVO.setEnterpriseName(x.getEnterpriseName());
            subClientListVO.setCreateTime(DateUtil.longToString(Long.valueOf(x.getCreateTime()), "yyyy-MM-dd HH:mm:ss"));
            return subClientListVO;
        }).collect(Collectors.toList()));
    }

    private Specification<UserEntity> assembleSubClientQueryCondition(String keyword) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            predicates.add(criteriaBuilder.equal(root.get("creatorId"), AuthenticationHolder.getUserId()));
            if (StringUtils.isNotBlank(keyword)) {
                List<Predicate> likeCon = new ArrayList<>();
                likeCon.add(criteriaBuilder.like(root.get("phone"), "%" + keyword + "%"));
                likeCon.add(criteriaBuilder.like(root.get("username"), "%" + keyword + "%"));
                likeCon.add(criteriaBuilder.like(root.get("enterpriseName"), "%" + keyword + "%"));
                Predicate[] likeArr = new Predicate[likeCon.size()];
                predicates.add(criteriaBuilder.or(likeCon.toArray(likeArr)));
            }
            Predicate[] preArr = new Predicate[predicates.size()];
            return criteriaBuilder.and(predicates.toArray(preArr));
        };
    }

    private void afterEnterpriseNameModified(String id, String newName) {
        List<UserEntity> allByMerchantId = userEntityRepo.findAllByMerchantId(id);
        for (UserEntity userEntity : allByMerchantId) {
            userEntity.setMerchantName(newName);
        }
        userEntityRepo.saveAll(allByMerchantId);
        List<DeviceEntity> allDevice = deviceEntityRepo.findAllByUserId(id);
        for (DeviceEntity deviceEntity : allDevice) {
            deviceEntity.setBelongMerchantName(newName);
        }
        deviceEntityRepo.saveAll(allDevice);
    }

    private void afterUsernameModified(String id, String newName) {
        Map<String, CreatorPersistenceService> creatorPersistenceServiceMap = applicationContext.getBeansOfType(CreatorPersistenceService.class);
        for (CreatorPersistenceService creatorPersistenceService : creatorPersistenceServiceMap.values()) {
            creatorPersistenceService.updateCreatorNameByCreatorId(id, newName);
        }
    }

    private void afterCreatorDeleted(String id) {
        Map<String, CreatorPersistenceService> creatorPersistenceServiceMap = applicationContext.getBeansOfType(CreatorPersistenceService.class);
        for (CreatorPersistenceService creatorPersistenceService : creatorPersistenceServiceMap.values()) {
            creatorPersistenceService.updateCreator(id);
        }
    }

}
